#include "server.h"


int server_worker(int netfd, MYSQL *mysql)
{
    // 第一次连接时初始化server_path、client_path(整个网盘连接时最开始初始化一次)
    azu_factory_t factor;
    //清空结构体factor
    bzero(&factor, sizeof(factor));
    //初始化后的mysql赋值结构体中的server_db
    factor.server_db = mysql;
    //初始化server.path、client.path
    char addr[1024]={0};
    getcwd(addr,sizeof(addr));
    strcpy(factor.server_path,addr);// 赋值一开始的服务端绝对路径
    strcpy(factor.client_path,"/");// 赋值一开始的客户端看见的虚拟路径
    strcpy(factor.username, "test2");
    // 开始循环接受命令
    while(1)
    {
        char buf[4096];
        bzero(buf, sizeof(buf));        
        ssize_t sret = recv(netfd, buf, sizeof(buf), 0);// 接受用户发的消息
        if(sret == 0)
        {
            printf("客户端已断开！\n");
            break;
        }

        char *command = strtok(buf, " ");// 存放用户发送的“命令 路径/文件”中的“命令”
        char *path = strtok(NULL, "\n");// 存放用户发送“命令 路径/文件”中的“路径/文件”

        bzero(factor.command,sizeof(factor.command));// 清空factor.command
        bzero(factor.input_path,sizeof(factor.input_path));// 清空input_path

        if (path == NULL)// 若用户只发送“命令”未跟路径或文件，如“ls”、“pwd”
        {
            strncpy(factor.command, command, strlen(command) - 1);// 赋值factor.command（去除\n）
        }
        else
        {
            strcpy(factor.command, command);// 赋值factor.command
            strcpy(factor.input_path, path);// 赋值input_path
        }

        printf("客户端传来的命令 : %s ", factor.command);
        printf("客户端传来的路径 : %s\n", factor.input_path);
        
        command_execute(netfd, &factor);
        printf("命令结束后的服务端绝对路径 : %s\n", factor.server_path);
    }
    return 0;
}

int command_execute(int netfd, azu_factory_t *factor)
{ 
    int len = strlen(factor->command);
    printf("len = %d\n", len);
    if(len == 2)
    {
        if(strcmp(factor->command,"cd")==0){
            printf("//cd worker\n");                    //
            worker_cd(netfd, factor);
        }
        else if(strcmp(factor->command,"ls")==0){
            printf("//ls worker\n");
            worker_ls(netfd,factor);
        }
        else{
            return -1;
        }
    }
    else if(len == 3){
        if(strcmp(factor->command,"pwd")==0){
            printf("//pwd worker\n");                    //
            worker_pwd(netfd,factor);        
        }
        else{
            return -1;
        }
    }
    else if(len == 4){
        if(strcmp(factor->command,"puts")==0){
            printf("//puts worker\n");                    //
            //worker_puts_server(netfd,factor);
        }
        else if(strcmp(factor->command,"gets")==0){
            printf("//gets worker\n");                    //
            worker_gets_server(netfd,factor);
        }
        else{
            return -1;
        }
    }
    else if(len == 5){
        if(strcmp(factor->command,"mkdir")==0){
            printf("//mkdir worker\n");                    //
            worker_mkdir(netfd,factor);
        }
        else if(strcmp(factor->command,"rmdir")==0){
            worker_rmdir(netfd,factor);                   //
        }
        else{
            return -1;
        }
    }
    else if(len == 6){
        if(strcmp(factor->command,"remove")==0)
        {
            printf("//remove worker\n");                    //
            worker_remove(netfd, factor);
        }
        else{
            return -1;
        }
    }
    else{
        return -1;
    }

    return 0;
}

//------------------------------------------------------------------------------------------
// 王
int worker_gets_server(int netfd,azu_factory_t *factor)
{
    if (factor == NULL || factor->server_db == NULL || factor->client_path == NULL || factor->input_path == NULL) 
    {
        printf("Invalid input parameter.\n");
        return -1;
    }
    
    int len = strlen(factor->client_path);
    char client_path[len+1];
    strcpy(client_path,factor->client_path);
    len = strlen(factor->input_path);
    char input_path[len+1];
    strcpy(input_path,factor->input_path);
    char path[1024];
    strcpy(path,client_path);                                         
    strcat(path,input_path);
  
    char sql[4096] = {0};
    int sret=sprintf(sql,"select MD5 from files where path ='%s' and user = '%s';", path, factor->username);
    if (sret < 0) {
        printf("Failed to generate SQL query.\n");
    } else if (sret >= sizeof(sql)) {
        printf("SQL query is too long.\n");
    } else {
        printf("Generated SQL query: %s\n", sql);
    }

    int qret = mysql_query(factor->server_db,sql);
    if(qret != 0){
        printf("Error: %s\n", mysql_error(factor->server_db));
        return -1;
    }

    MYSQL_RES *result = mysql_store_result(factor->server_db);
    MYSQL_ROW row;
    row = mysql_fetch_row(result);

    char *md5=row[0];
    len = strlen(factor->input_path);
    char buf[len+1];
    memcpy(buf,factor->input_path,len+1);

    train_t train;
    bzero(&train,sizeof(train_t));
    train.length=strlen(buf);
    strcpy(train.data,buf);
    send(netfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);   

    long client_st_size = 0;
    recv(netfd,&client_st_size,sizeof(long),0);       //接收文件长度
    printf("偏移量  :%ld\n",client_st_size);          
    int fd = open(md5,O_RDWR);
    FILE *fp = fdopen(fd, "r+");
    fseek(fp,client_st_size,0);                     //若文件存在   偏移到断点位置

    ERROR_CHECK(fd,-1,"open");
    struct stat statbuf;
    fstat(fd,&statbuf);
    printf("filesize = %ld\n", statbuf.st_size);
    train.length = sizeof(statbuf.st_size);
    memcpy(train.data,&statbuf.st_size,train.length);
    send(netfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    long limit=100*1024*1024;

    if(statbuf.st_size>limit){
        printf("文件大于100M!\n");
        // mmap 
        char *p = (char *)mmap(NULL,statbuf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        ERROR_CHECK(p,MAP_FAILED,"mmap");
        // send
        send(netfd,p,statbuf.st_size,MSG_NOSIGNAL);
        munmap(p,statbuf.st_size);  
    }
    else{
        printf("文件小于100M\n");
        sendfile(netfd,fd,NULL,statbuf.st_size);
    }
    LOG_OPERATION();
    return 0;
}



int worker_mkdir(int netfd,azu_factory_t *factor){
    if (factor == NULL || factor->server_db == NULL || factor->client_path == NULL || factor->input_path == NULL) 
    {
        printf("Invalid input parameter.\n");
        return -1;
    }

    int len = strlen(factor->client_path);
    char client_path[len+1];
    strcpy(client_path,factor->client_path);
    len = strlen(factor->input_path);
    char input_path[len+1];
    strcpy(input_path,factor->input_path);


    char sql[4096]={0}; 
    int sret=sprintf(sql,"select id from files where (path ='%s' and user = '%s') or (path ='%s' and user = 'home') ;",client_path,factor->username,client_path);
    if (sret < 0) {
        printf("Failed to generate SQL query.\n");
    } else if (sret >= sizeof(sql)) {
        printf("SQL query is too long.\n");
    } else {
        printf("Generated SQL query: %s\n", sql);
    }

    int qret = mysql_query(factor->server_db,sql);
    if(qret != 0){
        printf("Error: %s\n", mysql_error(factor->server_db));
        return -1;
    }

    MYSQL_RES *result = mysql_store_result(factor->server_db);
    MYSQL_ROW row;
    row = mysql_fetch_row(result);
    char *pre_pid=row[0];
    int pid = atoi(pre_pid);

    char path[1024];

    strcpy(path,client_path);
    printf("path1 = %s\n",path);
    if(pid!=1){
        strcat(path,"/");
    }
    printf("path1 = %s\n",path);
    strcat(path,input_path);
    printf("path1 = %s\n",path);
    sret=sprintf(sql,"insert into files(name,type,pre_pid,path,user,tomb) values('%s','d','%s','%s','test2',0);",input_path,pre_pid,path);
    if (sret < 0) {
        printf("Failed to generate SQL query.\n");
    } else if (sret >= sizeof(sql)) {
        printf("SQL query is too long.\n");
    } else {
        printf("Generated SQL query: %s\n", sql);
    }
    qret = mysql_query(factor->server_db,sql);
    if(qret != 0){
        printf("Error: %s\n", mysql_error(factor->server_db));
        return -1;
    }
    //创建成功发送消息给客户端
    int length = strlen("Directory Create Successfully!");
    send(netfd, &length, sizeof(length), 0);
    send(netfd, "Directory Create Successfully!", 30, 0);
    LOG_OPERATION();
    return 0;
} 


int worker_rmdir(int netfd,azu_factory_t *factor){


     if (factor == NULL || factor->server_db == NULL || factor->client_path == NULL || factor->input_path == NULL) 
    {
        printf("Invalid input parameter.\n");
        return -1;
    }

    int len = strlen(factor->client_path);
    char client_path[len+1];
    strcpy(client_path,factor->client_path);
    len = strlen(factor->input_path);
    char input_path[len+1];
    strcpy(input_path,factor->input_path);


    char path[1024];
    strcpy(path,client_path);
    strcat(path,input_path);

    char sql[1024]={0}; 
    int sret=sprintf(sql,"update files set tomb = 1 where path = '%s' and user = 'test2';",path);
    if (sret < 0) {
        printf("Failed to generate SQL query.\n");
    } else if (sret >= sizeof(sql)) {
        printf("SQL query is too long.\n");
    } else {
        printf("Generated SQL query: %s\n", sql);
    }
    int qret = mysql_query(factor->server_db,sql);
    if(qret != 0){
        printf("Error: %s\n", mysql_error(factor->server_db));
        return -1;
    }
    //删除成功发送消息给客户端
    int length = strlen("Directory Remove Successfully!");
    send(netfd, &length, sizeof(length), 0);
    send(netfd, "Directory Remove Successfully!", 30, 0);
    LOG_OPERATION();
    return 0;  

}

//----------------------------------------------------------------------------------------------------------------------------

// 费
int worker_ls(int netfd, azu_factory_t *factor)
{
    //测试
    strcpy(factor->username, "test2");
    //strcpy(factor->client_path, "/dir1");
    
    //获得mysql句柄
    MYSQL *mysql = factor->server_db;

    //传递要执行sql的语句
    char sql[4096];
    int sret = 0;
    if (strcmp(factor->client_path, "/") == 0)
    {
        sret = sprintf(sql,"select name from files where pre_pid = 1;");
    }
    else
    {
        sret = sprintf(sql,"select name from files where user = '%s' and tomb = 0 and path like '%s/%%';", factor->username, factor->client_path);
    }

    if (sret != (strlen(sql)))
    {
        printf("Failed to generate SQL query.\n");
        //ls失败发送消息给客户端
        int length = strlen("ls Failed!");
        send(netfd, &length, sizeof(length), 0);
        send(netfd, "ls Failed!", 10, 0);
        return -1;
    }

    int qret = mysql_query(mysql, sql);
    if (qret != 0)
    {
        fprintf(stderr, "error:%s\n", mysql_error(mysql));
        //ls失败发送消息给客户端
        int length = strlen("ls Failed!");
        send(netfd, &length, sizeof(length), 0);
        send(netfd, "ls Failed!", 10, 0);
        return -1;
    }

    //获取sql返回的信息
    MYSQL_RES *res = mysql_store_result(mysql);
    MYSQL_ROW row;
    char buf[4096] = {"Files and Directories:\n"};
    while((row = mysql_fetch_row(res)) != NULL)
    {
        strcat(buf, row[0]);
        strcat(buf, "\n");
    }
    
    // 发送给客户端
    int length = strlen(buf);
    send(netfd, &length, sizeof(length), 0);
    send(netfd, buf, length, 0);
    LOG_OPERATION();
    return 0;
}

// 费
int worker_pwd(int netfd, azu_factory_t *factor)
{
    //测试
    //strcpy(factor->client_path, "/dir1");

    //将client_path赋值给buf
    char buf[4096];
    bzero(buf, sizeof(buf));
    strcat(buf, factor->client_path);

    // 发送给客户端
    int length = strlen(buf);
    send(netfd, &length, sizeof(length), 0);
    send(netfd, buf, length, 0);
    LOG_OPERATION();
    return 0;
}

// 费
int worker_remove(int netfd, azu_factory_t *factor)
{
    //测试
    strcpy(factor->username, "test2");
    //strcpy(factor->client_path, "/dir1");
    
    //获得mysql句柄
    MYSQL *mysql = factor->server_db;

    //获取当前目录的id
    char sql[4096];
    int sret = sprintf(sql,"select id from files where user = '%s' and path = '%s';", factor->username, factor->client_path);
    if (sret != (strlen(sql)))
    {
        printf("Failed to generate SQL query.\n");
        //删除失败发送消息给客户端
        int length = strlen("Remove Failed!");
        send(netfd, &length, sizeof(length), 0);
        send(netfd, "Remove Failed!", 20, 0);
        return -1;
    }

    int qret = mysql_query(mysql, sql);
    if (qret != 0)
    {
        fprintf(stderr, "error:%s\n", mysql_error(mysql));
        //删除失败发送消息给客户端
        int length = strlen("Remove Failed!");
        send(netfd, &length, sizeof(length), 0);
        send(netfd, "Remove Failed!", 20, 0);
        return -1;
    }

    MYSQL_RES *res = mysql_store_result(mysql);
    MYSQL_ROW row;
    char pre_pid[1024];
    bzero(pre_pid, sizeof(pre_pid));
    while((row = mysql_fetch_row(res)) != NULL)
    {
        strcat(pre_pid, row[0]);
    }
    int pid = atoi(pre_pid);

    //删除文件（更新对应文件的tomb = 1）
    char sql_update[40960];
    sret = sprintf(sql_update,"update files set tomb = 1 where (user = '%s' and type = 'f' and path = '%s') or"
                              " (user = '%s' and type = 'f' and pre_pid = %d and name = '%s');", 
                              factor->username, factor->input_path,  factor->username, pid, factor->input_path);
    if (sret != (strlen(sql_update)))
    {
        printf("Failed to generate SQL query.\n");
        //删除失败发送消息给客户端
        int length = strlen("Remove Failed!");
        send(netfd, &length, sizeof(length), 0);
        send(netfd, "Remove Failed!", 20, 0);
        return -1;
    }

    qret = mysql_query(mysql, sql_update);
    if (qret != 0)
    {
        fprintf(stderr, "error:%s\n", mysql_error(mysql));
        //删除失败发送消息给客户端
        int length = strlen("Remove Failed!");
        send(netfd, &length, sizeof(length), 0);
        send(netfd, "Remove Failed!", 20, 0);
        return -1;
    }
    
    //删除成功发送消息给客户端
    int length = strlen("Remove Successfully!");
    send(netfd, &length, sizeof(length), 0);
    send(netfd, "Remove Successfully!", 20, 0);
    LOG_OPERATION();
    return 0;
}


//------------------------------------------------------------------------------------------------------------------------
// 刘
//客户根目录
int root_path(azu_factory_t *factor)
{
   factor->client_path[0]='/';
   factor->client_path[1]='\0';
   return 0;
}
//绝对路径
int abs_path(azu_factory_t*factor)
{
    bzero(factor->client_path,sizeof(factor->client_path));
    strcpy(factor->client_path,factor->input_path);
    return 0;
}
//上一级目录
int parent_path(azu_factory_t*factor)
{
    //如果当前目录为根目录 返回
    if(factor->client_path[0]=='/'&&factor->client_path[1]=='\0')
    {
        return 0;
    }
    int i=strlen(factor->client_path);
    //printf("i=%d",i);
    int count=0,j=i;
    while(factor->client_path[i-2]!='/')
    {
        i--;
        count++;
    }
    factor->client_path[j-count-1]='\0';  
    return 0;
}
//cd后查看sql虚拟文件表 当前目录是否存在
int check_path(const azu_factory_t *factor,MYSQL* mysql)
{
    char stmt_str[512]={0};
    char temp[408]={0};
    strcpy(temp,factor->client_path);
    //sprintf只能拼接3-1003个字节
    sprintf(stmt_str,"%s'%s'","select *from files where path=",temp);
    printf("stmt_str=%s\n",stmt_str);
    int qret= mysql_query(mysql,stmt_str);
    MYSQL_RES *res = mysql_store_result(mysql);
    MYSQL_ROW row;
    if ((row = mysql_fetch_row(res)) == NULL)
    {
        return -1;
    }
    return 0;
}
//相对路径
int rel_client(azu_factory_t *factor)
{
    strcat(factor->client_path,factor->input_path);
    return 0;
}
//如果路径的后面没有/就加上
int add_underlien(azu_factory_t*factor)
{
    int length=strlen(factor->client_path);
    if(factor->client_path[0]=='/'&&factor->client_path[1]=='\0')
    {
        return 0;
    }
    if(factor->client_path[length-2]!='/')
    {
        factor->client_path[length]='/';
        factor->client_path[length+1]='\0';
    }
    return 0;
}
//删除cd ./路径 中的./
int del_point_underlien(azu_factory_t*factor)
{
    int i=0,j=2;
    while(factor->input_path[i]!='\0')
    {
        factor->input_path[i]=factor->input_path[j];
        i++;
        j++;
    }
    return 0;
}
int worker_cd(int netfd,azu_factory_t*factor)                                                                                                                                                    
{   
    MYSQL *mysql = factor->server_db;
    char client_path[1024]={0};
    char buf[1024]={0};
    strcpy(client_path,factor->client_path);
    switch(factor->input_path[0])
    {
    case '.':
        //cd ..
        if(factor->input_path[1]=='.'&&factor->input_path[2]=='\0')
        {
            parent_path(factor);
        }
        //cd ./相对路径
        //cd ./相对路径  与cd 相对路径 效果一样 Linux会在cd 相对路径 中自动补充./
        else if(factor->input_path[1]=='/'&&factor->input_path[2]!='\0')
        {
            del_point_underlien(factor);
            add_underlien(factor);
            rel_client(factor);
        }
        break;
    case'\0'://cd 
        root_path(factor);
       break;
    case '/'://cd 绝对路径
        abs_path(factor);
        break;
    default://cd 相对路径
        add_underlien(factor);
        rel_client(factor);
        break;
    }
    
    //cd后检测当前目录是否存在 如果存在给客户端发送目录信息 如果不存在 发送目录不存在
    if(check_path(factor,mysql)==0)
    {
        strcpy(buf,factor->client_path);
        // 发送给客户端
        int length = strlen(buf);
        send(netfd, &length, sizeof(length), 0);
        send(netfd, buf, length, 0);
    }
    else
    {
        //发送消息给客户端
        int length = strlen("Directory Inexistence!");
        send(netfd, &length, sizeof(length), 0);
        send(netfd, "Directory Inexistence!", 22, 0);
        //将目录还原
        strcpy(factor->client_path,client_path);
    }
    printf("相对 %s\n",factor->client_path);
    LOG_OPERATION();
    return 0;
}

//-----------------------------------------------------------------------------
//武
//武 进行mysql查表并返回需要的表项
int Query(char * query,char *retval)
{
	//retval是传入传出参数，传入一个提前开辟的空数组，返回一个所需的包含换行的数组
    MYSQL *conn;
	MYSQL_RES *res;
	MYSQL_ROW row;
	unsigned int t;

	t=mysql_query(conn,query);//Zero for success. Nonzero if an error occurred.

	if(t)
	{
        printf("Error making query:%s\n",mysql_error(conn));  
        return -1;
    }
    else{
        res=mysql_use_result(conn);
        if(res)
        {
            if((row=mysql_fetch_row(res)) == NULL) {
                /* printf("Don't find data\n");//if query statement is wroing,this function will return NULL? */
                return -1;
            } 
            //循环挨行拼接行信息进retval数组
            while(row) {
                for (t=0; t<mysql_num_fields(res); t++) {
                    if(strlen(retval) == 0) {
                        strncpy(retval,row[t],strlen(row[t]));
                    } else {
                        sprintf(retval,"%s %s",retval,row[t]);
                    }
                }
                sprintf(retval,"%s%s",retval,"\0");
                row=mysql_fetch_row(res);
                if(row) {
                    sprintf(retval,"%s%s",retval,"\n");
                }   
            }
        }else{
            printf("Don't find data\n");
            return -1;
        }
        //关闭MYSQL_RES
        mysql_free_result(res);
    }
    mysql_close(conn);
    return 0;
}


//生成随机数
int GenerateStr(char *str) {
	int i,flag;
	srand(time(NULL));
	for(i = 0; i < STR_LEN; i ++) {
		flag = rand()%3;
		switch(flag) {
			case 0: str[i] = rand()%26 + 'a'; break;
			case 1: str[i] = rand()%26 + 'A'; break;
			case 2: str[i] = rand()%10 + '0'; break;
		}
	}
	//printf("%s\n", str);//输出生成的随机数。
	return 0;
}

//武 客户端注册程序
int s_regist(int sfd, char *buf)
{
	char* username = (char*)calloc(128, sizeof(char));
	char* upassword = (char*)calloc(128, sizeof(char));
	//char userpath[20] = "newdist/";
    
    	//printf("buf = %s\n",buf);

    username = strtok(buf, " ");// 放username
    upassword = strtok(NULL, "\n");// 存放当前user的密码

    	printf("username = %s\n", username);
    	printf("upassword = %s\n", upassword);
	
	int confirm = 0;
	char query[128] = "select * from users where username = '";
	char retval[128] = {0};
	sprintf(query, "%s%s%s", query, username, "'");
	int ret = Query(query, retval);
	if(ret == 0){
		printf("username already exists!\n");
		send(sfd, &confirm, 4, 0);
	}


    char values[128] = {0};
	char salt[STR_LEN + 1] = {0};
	GenerateStr(salt);
	printf("salt = %s\n", salt);
	char *spassword = crypt(upassword, salt);
	printf("spassword = %s\n", spassword);
	//strcat(userpath,username);
	//printf("userpath = %s\n", userpath);
	sprintf(values, "(\'%s\',\'%s\',\'%s\',\'%s\')",username, salt, spassword, username);
    
	char query2[256]="insert into users (username, salt, cryptpasswd, pwd) values ";
	strcat(query2, values);
    	//printf("%s\n", query2);
	int queryResult;

	confirm = 1;
	send(sfd, &confirm, 4, 0);
	return 1;
}



//武 服务端登录函数
int s_login(int netfd, MYSQL* mysql, int* isLogin)
{
    char buf[1024];
    bzero(buf, sizeof(buf));
    
    //接收登录
    printf("wait recv\n");
    int length = 0;
    recv(netfd, &length, 4, MSG_WAITALL);
    char *p = (char *)buf;
    int total = 0;
    while(total < length){
        ssize_t sret = recv(netfd,p+total,length-total,0);
        total += sret;
    }   	
    printf("buf = %s\n", buf);        

    char* username = calloc(1024, sizeof(char));
	char* upassword = calloc(1024, sizeof(char));

        
    username = strtok(buf, " ");// 放usename
    upassword = strtok(NULL, "/0");// 存放当前user的密码
    printf("username = %s, upassword = %s\n", username, upassword);

	char query[300] = "select * from users where username = '";
	unsigned int queryRet;
	sprintf(query, "%s%s%s", query, username, "'");

    //在输出前先打印查询语句
	puts(query);

    //把SQL语句传递给MySQL
	queryRet = mysql_query(mysql, query);
	MYSQL_ROW row;
    MYSQL_RES *res;
    if(queryRet)
	{
		printf("Error making query\n");
		int confirm = 0;
		send(netfd, &confirm, 4, 0);
	}
	else
	{
        //第一种判断方式
        res = mysql_store_result(mysql);
        printf("mysql_num_rows = %lu\n", (unsigned long)mysql_num_rows(res));

        //第二种判断方式,两种方式不能一起使用
		/* res = mysql_use_result(conn); */
        row = mysql_fetch_row(res);
    }
	if(row == NULL){
        printf("Don't find any data\n");
		int confirm = 0;
		send(netfd, &confirm, 4, 0);
    }else{

			char *salt = row[2];
			printf("salt = %s\n",salt);
			char *cryptpassword = row[3];
			printf("cryptpassword = %s\n",cryptpassword);
			char *cpw = crypt(upassword,salt);
			printf("cpw = %s\n", cpw);
			
			int ret = 0;
            if(ret == 0){
				//登录成功
				int confirm = 1;
				send(netfd, &confirm, 4, 0);
				*isLogin = 1;
			}else{
				//登录失败
				int confirm = 0;
				send(netfd, &confirm, 4, 0);
			}
		}
	mysql_free_result(res);
    return 0;   
}